home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / video / fly8111-.000 / fly8111- / fly8 / memory2.c < prev    next >
C/C++ Source or Header  |  1979-12-31  |  15KB  |  730 lines

  1. /* --------------------------------- memory.c ------------------------------- */
  2.  
  3. /* This is part of the flight simulator 'fly8'.
  4.  * Author: Eyal Lebedinsky (eyal@ise.canberra.edu.au).
  5. */
  6.  
  7. /* A general purpose memory manager.
  8.  *
  9.  * This one keeps a small header in front of all blocks (both free and
  10.  * allocated) to enable merging adjacent ones. A separate quick access
  11.  * by size list is also maintained for the allocation part. It does not
  12.  * use the size in the free() call since it has it's own.
  13.  *
  14.  * Memory usage is a 512 entries table of BLOCK (one pointer each) and two
  15.  * shorts for each memory block (suppose we have a 1000 blocks, then we use
  16.  * 4kb for block headers and 2kb for the fixed table).
  17. */
  18.  
  19. #include "fly.h"
  20.  
  21.  
  22. #define GRAIN        8
  23.  
  24. #define NBLOCKS        512
  25.  
  26. #define MAXBLOCK    (NBLOCKS*GRAIN)
  27.  
  28. #define BLOCKHEADER    (2 * sizeof (SIZE (NULL)))
  29.  
  30. #define MAXBYTES    (MAXBLOCK - BLOCKHEADER)
  31.  
  32. #define MINBYTES    (BLOCKHEADER + 2 * sizeof (NEXT (NULL)))
  33.  
  34. #define BYTESINDEX(n) \
  35.     ((Uint)(((n) < MINBYTES ? MINBYTES : (n)) - 1) / GRAIN)
  36.  
  37. #define INDEXSIZE(i)    (((i) + 1) * GRAIN)
  38.  
  39. #define CHUNKHEADER    (offsetof (CHUNK, buff) + GRAIN)
  40.  
  41. #define CHUNKSIZE    (MAXBLOCK*1)
  42.  
  43. #define CHUNKMIN    (CHUNKSIZE/4)
  44.  
  45. #define CHUNKPART    (CHUNKMIN/2)
  46.  
  47. #define MEM_MINLOG    0            /* log nothing */
  48.  
  49. #define SIZE(p)        ((short *)(p))[-1]
  50. #define PSIZE(p)    ((short *)(p))[-2]
  51. #define NEXT(p)        ((char **)(p))[0]
  52. #define PREV(p)        ((char **)(p))[1]
  53.  
  54. #define DLLADD(n,b) \
  55.     do { \
  56.         char    *z; \
  57.         if (T(z = NEXT (b) = blocks[n].chain)) { \
  58.             PREV (b) = PREV (z); \
  59.             PREV (z) = b; \
  60.         } else \
  61.             PREV (b) = (char *)&blocks[n].chain; \
  62.         blocks[n].chain = b; \
  63.     } while (0)
  64.  
  65. #define DLLREMOVE(b) \
  66.     do { \
  67.         char    *z; \
  68.         if (T(z = NEXT (b))) \
  69.             PREV (z) = PREV (b); \
  70.         NEXT (PREV (b)) = z; \
  71.     } while (0)
  72.  
  73. #define DLLTRIMHEAD(n,b) \
  74.     do { \
  75.         char    *z; \
  76.         if (T(z = blocks[n].chain = NEXT (b))) \
  77.             PREV (z) = PREV (b); \
  78.     } while (0)
  79.  
  80. /* memory is acquired in large chunks to later be carved into allocated
  81.  * blocks.
  82.  *
  83.  * chuncks are stored as a list of blocks. The first one is pointed at
  84.  * by chunk->chain, then each block follows on block->next. The last block
  85.  * on a chunk list is always a dummy block with a size of zero (it only has
  86.  * an header, no body).
  87. */
  88. typedef struct chunk    CHUNK;
  89. struct chunk {
  90.     CHUNK    *next;
  91.     Ushort    size;
  92.     long    buff[1];            /* need to force alignment */
  93. };
  94.  
  95. /* a block has a header with the block (net) size and the size of the previous
  96.  * block (as the dll ptr). Free blocks are linked to another list (based on
  97.  * the block size) using block->next;
  98. */
  99.  
  100. /* Free blocks are kept on a list by size. It is just a simple pointer which
  101.  * may have some stats attached.
  102. */
  103. typedef struct blocks    BLOCKS;
  104. struct blocks {
  105.     char    *chain;        /* note: sometimes used as NEXT(block)!!! */
  106. #ifdef MEM_STATS
  107.     short    nused;                /* stats: good alloc - free */
  108.     Ulong    nalloc;                /* stats: requested alloc */
  109.     Ulong    nomem;                /* stats: memory short count */
  110. #endif
  111. };
  112.  
  113. static BLOCKS    *blocks = 0;
  114. static CHUNK    *chunks = 0;            /* used very little */
  115. static Uint    malloc_dead = 0;        /* malloc failed! */
  116.  
  117. static int    debugging = 0;            /* internal, leave alone */
  118. static int    logging = 0;            /* set to 1 for logging */
  119.  
  120. /* some debugging functions first
  121. */
  122. LOCAL_FUNC void * NEAR
  123. verify_address (char *p, char *title)
  124. {
  125.     CHUNK    *c;
  126.  
  127.     if ( SIZE (p) <= 0 ||  SIZE (p) > MAXBLOCK ||
  128.         PSIZE (p) <  0 || PSIZE (p) > MAXBLOCK) {
  129.         LogPrintf ("verify_address(%s)> bad %d/%p P %d\n",
  130.             title, SIZE (p), p, PSIZE (p));
  131.         return (NULL);
  132.     }
  133.     for (c = chunks; c; c = c->next) {
  134.         if (p            >= GRAIN           + (char *)c->buff &&
  135.             SIZE (p) + p <= GRAIN + c->size + (char *)c->buff)
  136.             return (p);
  137.     }
  138.     LogPrintf ("verify_address(%s)> stray %d/%p P %d\n",
  139.         title, SIZE (p), p, PSIZE (p));
  140.     return (NULL);
  141. }
  142.  
  143. LOCAL_FUNC void NEAR
  144. mem_assert (char *title)
  145. {
  146.     int    i;
  147.     int    n;
  148.     int    t;
  149.     char    *p;
  150.     long    freebytes = 0;
  151.     CHUNK    *c;
  152.  
  153.     if (!blocks)
  154.         return;
  155.  
  156.     freebytes = STATS_MEMALLOCED;
  157.     for (i = 0; i < NBLOCKS; ++i) {
  158.         n = INDEXSIZE (i);
  159.         for (p = blocks[i].chain; p; p = NEXT (p)) {
  160.             if (SIZE (p) !=  (short)n)
  161. LogPrintf ("assert(%s): %5d - Bad %d/%p P %d\n",
  162.                     title, n, SIZE (p), p, PSIZE (p));
  163.             freebytes += n;
  164.         }
  165.     }
  166.     if (T(freebytes -= STATS_MEMTOTAL))
  167.         LogPrintf ("assert(%s): leakage %9ld\n", title, -freebytes);
  168.     i = 0;
  169.     for (c = chunks; c; c = c->next) {
  170.         ++i;
  171.         t = 0;
  172.         for (n = 0, p = GRAIN + (char *)c->buff;;) {
  173.             if (PSIZE (p) != n) {
  174. LogPrintf ("assert(%s): %d[%ld]: bad P %d (!= %d)\n",
  175.                     title, i, t, PSIZE (p), n);
  176.                 break;
  177.             }
  178.             if (0 == (n = SIZE (p)))
  179.                 break;
  180.             if (n < 0)
  181.                 n = -n;
  182.             p += n;
  183.             t += n;
  184.         }
  185.         if ((Ushort)t != c->size) {
  186.             LogPrintf ("assert(%s): %d: bad length %ld\n",
  187.                 title, i, t);
  188.         }
  189.     }
  190. }
  191.  
  192. /* A set of safe memory management functions.
  193. */
  194.  
  195. extern void * FAR
  196. xmalloc (Uint size)
  197. {
  198.     Ulong    flags;
  199.     void    *p;
  200.  
  201.     if (!malloc_dead) {
  202.         flags = Sys->Disable ();
  203.         if (F(p = malloc (size))) {
  204.             malloc_dead = 1;
  205.             ++STATS_MEMLOW;
  206.             ++STATS_MEMNO;
  207.         }
  208.         Sys->Enable (flags);
  209.     } else {
  210.         ++STATS_MEMLOW;
  211.         ++STATS_MEMNO;
  212.         p = NULL;
  213.     }
  214.     return (p);
  215. }
  216.  
  217. extern void * FAR
  218. xcalloc (Uint count, Uint size)
  219. {
  220.     Ulong    flags;
  221.     void    *p;
  222.  
  223.     if (!malloc_dead) {
  224.         flags = Sys->Disable ();
  225.         if (F(p = calloc (count, size))) {
  226.             malloc_dead = 1;
  227.             ++STATS_MEMLOW;
  228.             ++STATS_MEMNO;
  229.         }
  230.         Sys->Enable (flags);
  231.     } else {
  232.         ++STATS_MEMLOW;
  233.         ++STATS_MEMNO;
  234.         p = NULL;
  235.     }
  236.     return (p);
  237. }
  238.  
  239. extern char * FAR
  240. xstrdup (const char *s)
  241. {
  242.     char    *p;
  243.     Ulong    flags;
  244.  
  245.     flags = Sys->Disable ();
  246.     if (F(p = strdup (s))) {
  247.         malloc_dead = 1;
  248.         ++STATS_MEMLOW;
  249.         ++STATS_MEMNO;
  250.     }
  251.     Sys->Enable (flags);
  252.     return (p);
  253. }
  254.  
  255. extern void * FAR
  256. xfree (void *block)
  257. {
  258.     Ulong    flags;
  259.  
  260.     if (!block)
  261.         return (0);
  262.  
  263.     flags = Sys->Disable ();
  264.     free (block);
  265.     Sys->Enable (flags);
  266.     malloc_dead = 0;
  267.     return (0);
  268. }
  269.  
  270. /* We get here if the free blocks list cannot satisfy a memory allocation
  271.  * request.
  272. */
  273. LOCAL_FUNC int NEAR
  274. mem_chunk (void)
  275. {
  276.     CHUNK    *c;
  277.     char    *b;
  278.     int    n;
  279.  
  280.     for (n = CHUNKSIZE; n > CHUNKMIN; n -= CHUNKPART) {
  281.         if (T(c = (CHUNK *)malloc (CHUNKHEADER + GRAIN + n)))
  282.             break;
  283.     }
  284.  
  285.     if (F(c)) {
  286.         if (logging && !(st.flags1 & SF_ASYNC))
  287.             LogPrintf ("chunk malloc dead\n");
  288.         malloc_dead = 1;
  289.         ++STATS_MEMLOW;
  290.     } else {
  291.  
  292. /* add new chunk to chunks list.
  293. */
  294.         c->next = chunks;
  295.         chunks = c;
  296.         c->size = (Ushort)n;
  297.         STATS_MEMTOTAL += n;
  298.  
  299. /* build EOL block
  300. */
  301.         b = GRAIN + n + (char *)c->buff;
  302.         SIZE (b) = 0;
  303.         PSIZE (b) = (short)n;
  304.  
  305.         if (logging && !(st.flags1 & SF_ASYNC))
  306.             LogPrintf ("chunk EOL  is %d/%p\n", SIZE (b), b);
  307.  
  308. /* build root block.
  309. */
  310.         b = GRAIN + (char *)c->buff;
  311.         SIZE (b) = (short)n;
  312.         PSIZE (b) = 0;
  313.  
  314.         if (logging && !(st.flags1 & SF_ASYNC))
  315.             LogPrintf ("chunk root is %d/%p\n", SIZE (b), b);
  316.  
  317. /* add root to list by size.
  318. */
  319.         n = BYTESINDEX (n);
  320.         DLLADD (n, b);
  321.     }
  322.     return (malloc_dead);
  323. }
  324.  
  325. /* Try to satisfy a memory request by splitting a larger size block from
  326.  * the free memory list.
  327. */
  328. LOCAL_FUNC char * NEAR
  329. mem_split (Uint n)
  330. {
  331.     Uint    i;        /* index for block to split */
  332.     Uint    bytes;
  333.     char    *t;        /* block allocated */
  334.     char    *p;        /* block leftover */
  335.     char    *q;        /* following block */
  336.  
  337.     p = NULL;
  338.     for (i = n + 1 + BYTESINDEX (MINBYTES); ++i < NBLOCKS;) {
  339.         if (F(p = blocks[i].chain))
  340.             continue;
  341.  
  342.         if (logging && debugging && !(st.flags1 & SF_ASYNC))
  343.             LogPrintf ("split [%d] %d/%p", i, SIZE (p), p);
  344.  
  345. /* remove p from list by size.
  346. */
  347.         DLLTRIMHEAD (i, p);
  348.  
  349. /* we modify three blocks now, establish addressability.
  350. */
  351.         bytes = INDEXSIZE (n);            /* bytes allocated */
  352.         t = p;                    /* retained block */
  353.         q = t + SIZE (t);            /* next block */
  354.         p = q - bytes;                /* returned block */
  355.  
  356. /* update headers.
  357. */
  358.         PSIZE (q) = SIZE (p) = (short)bytes;
  359.         PSIZE (p) = (SIZE (t) -= (short)bytes);
  360.  
  361. /* add (leftover) t to list by size;
  362. */
  363.         i -= n + 1;
  364.         DLLADD (i, t);
  365.  
  366.         if (logging && debugging && !(st.flags1 & SF_ASYNC))
  367.             LogPrintf (" -> [%d] %d/%p + %d/%p\n",
  368.             i, SIZE (t), t, SIZE (p), p);
  369.         break;
  370.     }
  371.  
  372.     return (p);
  373. }
  374.  
  375. extern void * FAR
  376. mem_alloc (Uint bytes)
  377. {
  378.     char    *p;
  379.     Uint    i;
  380.     Ulong    flags;
  381.  
  382.     if (!bytes || !blocks) {
  383.         p = NULL;
  384.         goto ret;
  385.     }
  386.  
  387.     if (bytes > MAXBYTES) {
  388.         p = xmalloc (bytes);
  389.         goto ret;
  390.     }
  391.  
  392.     i = bytes + BLOCKHEADER;
  393.     i = BYTESINDEX (i);
  394.     flags = Sys->Disable ();
  395.  
  396. /* We get memory by first checking for a ready block, then trying to split
  397.  * a larger block, then trying to acquire fresh memory.
  398.  * The loop should terminate by either getting a block or my running out of
  399.  * system memory (malloc_dead).
  400. */
  401.     do {
  402.         if (T(p = blocks[i].chain))
  403.             DLLTRIMHEAD (i, p);
  404.         else {
  405.             ++STATS_MEMLOW;
  406.             if (T(p = mem_split (i)))
  407.                 ++STATS_MEMSPLIT;
  408.             else if (malloc_dead || mem_chunk ()) {
  409.                 ++STATS_MEMNO;
  410.                 break;
  411.             }
  412.         }
  413.     } while (F(p));
  414.  
  415. #ifdef MEM_STATS
  416.     ++blocks[i].nalloc;
  417.     if (p)
  418.         ++blocks[i].nused;
  419.     else
  420.         ++blocks[i].nomem;
  421. #endif
  422.     if (p) {
  423.         STATS_MEMALLOCED += SIZE (p);
  424.         if (STATS_MEMALLOCED > STATS_MEMMAXUSED)
  425.             STATS_MEMMAXUSED = STATS_MEMALLOCED;
  426.         SIZE (p) = -SIZE (p);            /* is now alloc'ed */
  427.     }
  428.  
  429.     Sys->Enable (flags);
  430. ret:
  431.     if (p)
  432.         memset (p, 0, bytes);
  433.     return (p);
  434. }
  435.  
  436. extern void * FAR
  437. memd_alloc (Uint bytes, char *file, int lineno)
  438. {
  439.     void    *p;
  440.  
  441.     debugging = 1;
  442.     p = mem_alloc (bytes);
  443.     debugging = 0;
  444.  
  445.     if (logging && !(st.flags1 & SF_ASYNC) && bytes >= MEM_MINLOG)
  446.         LogPrintf ("alloc %s(%d) %u/%p\n", file, lineno, bytes, p);
  447.  
  448.     return (p);
  449. }
  450.  
  451. /* A fast memory manager. A freed block is merged with neighbouring blocks
  452.  * rather than just get added to the pool.
  453. */
  454. extern void * FAR
  455. mem_free (void *block, int bytes)
  456. {
  457.     char    *p;
  458.     char    *t;
  459.     Ulong    flags;
  460.     int    n;
  461.  
  462.     if (!block || bytes < 0)
  463.         return (NULL);
  464.  
  465.     if (!bytes || bytes > MAXBYTES || !blocks)
  466.         return (xfree (block));
  467.  
  468.     flags = Sys->Disable ();
  469.  
  470.     p = block;
  471.     block = NEXT (block);            /* for the return(p) */
  472.  
  473. #ifdef CHECK_MEM
  474. if (-SIZE (p) < bytes ||
  475.     -SIZE (p) != INDEXSIZE (BYTESINDEX (BLOCKHEADER + bytes))) {
  476.     LogPrintf ("mem_free> %d bad block %d/%p\n", bytes, SIZE (p), p);
  477.     Sys->Enable (flags);
  478.     return (NULL);
  479. }
  480. #endif
  481.  
  482.     SIZE (p) = -SIZE (p);            /* is now free */
  483.  
  484.     bytes = SIZE (p);
  485.  
  486.     STATS_MEMALLOCED -= bytes;
  487.  
  488. #ifdef MEM_STATS
  489.     n = BYTESINDEX (bytes);
  490.     --blocks[n].nused;
  491.     if (logging && !(st.flags1 & SF_ASYNC) && blocks[n].nused < 0)
  492.         LogPrintf ("bad free count (%d)\n", bytes);
  493. #else
  494.     if (logging && !(st.flags1 & SF_ASYNC) && STATS_MEMALLOCED < 0)
  495.         LogPrintf ("bad free count (%d)\n", bytes);
  496. #endif
  497.  
  498. /* merge p into preceding blocks.
  499. */
  500.     while (PSIZE (p) && (t = p - PSIZE (p), SIZE (t) > 0)) {
  501.         DLLREMOVE (t);
  502.         SIZE (t) += SIZE (p);            /* merge p into t */
  503.         p = t;
  504.         ++STATS_MEMMERGE;
  505.     }
  506.  
  507. /* merge following blocks into p.
  508. */
  509.     while (t = p + SIZE (p), SIZE (t) > 0) {
  510.         DLLREMOVE (t);
  511.         SIZE (p) += SIZE (t);            /* merge t into p */
  512.         ++STATS_MEMMERGE;
  513.     }
  514.     PSIZE (t) = SIZE (p);
  515.  
  516. /* add p to list by size.
  517. */
  518.     n = BYTESINDEX (SIZE (p));
  519.     DLLADD (n, p);
  520.  
  521.     Sys->Enable (flags);
  522.  
  523.     return (block);
  524. }
  525.  
  526. extern void * FAR
  527. memd_free (void *block, int bytes, char *file, int lineno)
  528. {
  529.     if (logging && !(st.flags1 & SF_ASYNC) && bytes >= MEM_MINLOG)
  530.         LogPrintf ("free %s(%d) %u/%p\n",
  531.             file, lineno, bytes, block);
  532.  
  533.     return (mem_free (block, bytes));
  534. }
  535.  
  536. extern char * FAR
  537. mem_strdup (const char *s)
  538. {
  539.     char    *p;
  540.     int    len;
  541.  
  542.     if (s) {
  543.         if (T(p = mem_alloc (len = strlen (s) + 1)))
  544.             memcpy (p, s, len);
  545.     } else
  546.         p = NULL;
  547.     return (p);
  548. }
  549.  
  550. extern char * FAR
  551. memd_strdup (const char *s, char *file, int lineno)
  552. {
  553.     char    *p;
  554.     int    len;
  555.  
  556.     if (logging && !(st.flags1 & SF_ASYNC)
  557.                     && (!s || strlen(s)+1 >= MEM_MINLOG))
  558.         LogPrintf ("strdup %s(%d) \"%s\"\n", file, lineno,
  559.             s ? s : "(null)");
  560.  
  561.     if (s) {
  562.         if (T(p = memd_alloc (len = strlen (s) + 1, file, lineno)))
  563.             memcpy (p, s, len);
  564.     } else
  565.         p = NULL;
  566.     return (p);
  567. }
  568.  
  569. extern void * FAR
  570. mem_strfree (char *s)
  571. {
  572.     if (s)
  573.         memory_free (s, strlen (s) + 1);
  574.     return (NULL);
  575. }
  576.  
  577. extern void * FAR
  578. memd_strfree (char *s, char *file, int lineno)
  579. {
  580.     if (logging && !(st.flags1 & SF_ASYNC))
  581.         LogPrintf ("strfree %s(%d) \"%s\"\n", file, lineno,
  582.             s ? s : "(null)");
  583.  
  584.     if (s)
  585.         memd_free (s, strlen (s) + 1, file, lineno);
  586.     return (NULL);
  587. }
  588.  
  589. /* Ensure we are not low on memory. Not yet used.
  590. */
  591. extern void FAR
  592. mem_check (void)
  593. {
  594.     Ulong    flags;
  595.  
  596. #if 0
  597.     mem_assert ("mem_check");
  598. #endif
  599.     if (malloc_dead)
  600.         return;
  601.  
  602.     if (STATS_MEMTOTAL - STATS_MEMMAXUSED < CHUNKSIZE) {
  603.         flags = Sys->Disable ();
  604.         mem_chunk ();
  605.         Sys->Enable (flags);
  606.     }
  607. }
  608.  
  609. extern int FAR
  610. mem_init (void)
  611. {
  612.     int    i;
  613.  
  614.     STATS_MEMTOTAL = 0;
  615.     STATS_MEMMAXUSED = 0;
  616.     malloc_dead = 0;
  617.     chunks = NULL;
  618.     if (F(blocks = (BLOCKS *)xcalloc (NBLOCKS, sizeof (*blocks))))
  619.         return (1);
  620.  
  621.     for (i = 0; i < NBLOCKS; ++i) {
  622.         blocks[i].chain = NULL;
  623. #ifdef MEM_STATS
  624.         blocks[i].nalloc = 0;
  625.         blocks[i].nused = 0;
  626.         blocks[i].nomem = 0;
  627. #endif
  628.     }
  629.  
  630.     return (0);
  631. }
  632.  
  633. extern void FAR
  634. mem_term (void)
  635. {
  636.     char    *p;
  637.     CHUNK    *c, *c1;
  638.     int    i, n;
  639.     long    tt;
  640.     Ulong    size;
  641.     Ulong    totn;
  642.     Ulong    freebytes;
  643. #ifdef MEM_STATS
  644.     Ulong    totalloc;
  645.     Ulong    totnomem;
  646. #endif
  647.  
  648.     if (!blocks)
  649.         return;
  650.  
  651.     mem_assert ("mem_term");
  652.  
  653.     totn = freebytes = 0;
  654. #ifdef MEM_STATS
  655.     totalloc = totnomem = 0;
  656. #endif
  657.     LogPrintf ("Memory usage summary:\n\n");
  658.     LogPrintf ("Size  Count     Bytes   nAllocs   nFailed     nUsed\n");
  659.     for (i = 0; i < NBLOCKS; ++i) {
  660.         for (n = 0, p = blocks[i].chain; p; p = NEXT (p))
  661.             ++n;
  662. #ifdef MEM_STATS
  663.         if (n || blocks[i].nalloc) {
  664.             totalloc += blocks[i].nalloc;
  665.             totnomem += blocks[i].nomem;
  666. #else
  667.         if (n) {
  668. #endif
  669.             totn += n;
  670.             tt = INDEXSIZE (i) * (long)n;
  671.             freebytes += tt;
  672.             LogPrintf ("%4u %6u %9lu", INDEXSIZE (i), n, tt);
  673. #ifdef MEM_STATS
  674.             LogPrintf (" %9lu", blocks[i].nalloc);
  675.             if (blocks[i].nomem || blocks[i].nused)
  676.                 LogPrintf (" %9lu %9d",
  677.                     blocks[i].nomem, blocks[i].nused);
  678. #endif
  679.             LogPrintf ("\n");
  680.         }
  681.     }
  682.     LogPrintf (" tot %6lu %9lu", totn, freebytes);
  683. #ifdef MEM_STATS
  684.     LogPrintf (" %9lu %9lu", totalloc, totnomem);
  685. #endif
  686.     LogPrintf ("\nSize  Count     Bytes   nAllocs   nFailed     nUsed\n");
  687.  
  688.     n = 0;
  689.     size = 0;
  690.     LogPrintf ("\nChunk      Size Address range\n");
  691.     for (c1 = chunks; T(c = c1);) {
  692.         ++n;
  693.         LogPrintf ("%5d %9u %p-%p\n", n, c->size,
  694.             GRAIN + (char *)c->buff, c->size + (char *)c->buff);
  695.         c1 = c->next;
  696.         size += c->size;
  697.         xfree (c);
  698.     }
  699.     chunks = 0;
  700.     LogPrintf ("total %9lu\n\n", size);
  701.  
  702.     LogPrintf ("Max used %9lu\n", STATS_MEMMAXUSED);
  703.     LogPrintf ("Alloc'ed %9lu\n", STATS_MEMTOTAL);
  704.     LogPrintf ("Free     %9lu\n", freebytes);
  705.     LogPrintf ("Leakage  %9ld\n", STATS_MEMTOTAL - freebytes);
  706.  
  707.     blocks = xfree (blocks);
  708. }
  709.  
  710. #undef GRAIN
  711. #undef NBLOCKS
  712. #undef MAXBLOCK
  713. #undef BLOCKHEADER
  714. #undef MAXBYTES
  715. #undef MINBYTES
  716. #undef BYTESINDEX
  717. #undef INDEXSIZE
  718. #undef CHUNKHEADER
  719. #undef CHUNKSIZE
  720. #undef CHUNKMIN
  721. #undef CHUNKPART
  722. #undef MEM_MINLOG
  723. #undef SIZE
  724. #undef PSIZE
  725. #undef NEXT
  726. #undef PREV
  727. #undef DLLADD
  728. #undef DLLREMOVE
  729. #undef DLLTRIMHEAD
  730.